In .NET, a collection is any type
that supports the IEnumerable or
IEnumerable<T>
interface. All of the built-in collections in .NET, such as the array,
the list, and the stack, support these interfaces. A data contract can
include a collection as a data member, and a service contract can define operations that interact with
a collection directly. Because .NET collections are .NET-specific, WCF
cannot expose them in the service metadata, yet because they are so
useful, WCF offers dedicated marshaling rules for collections.
Whenever you define a service operation that uses the collection
interfaces IEnumerable<T>, IList<T>, or ICollection<T>,
the resulting metadata always uses an array. For example, this service
contract definition and implementation:
[ServiceContract]
interface IContactManager
{
[OperationContract]
IEnumerable<Contact> GetContacts();
...
}
class ContactManager : IContactManager
{
List<Contact> m_Contacts = new List<Contact>();
public IEnumerable<Contact> GetContacts()
{
return m_Contacts;
}
...
}
will still be exported as:
[ServiceContract]
interface IContactManager
{
[OperationContract]
Contact[] GetContacts();
}
1. Concrete Collections
If the collection in the contract is a concrete
collection (not an interface) and is a serializable collection—that
is, it is marked with the Serializable
attribute but not with the DataContract attribute—WCF can normalize the
collection automatically to an array of the collection’s type,
provided the collection contains an Add() method with
either one of these signatures:
public void Add(object obj); //Collection uses IEnumerable
public void Add(T item); //Collection uses IEnumerable<T>
For example, consider this contract definition:
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
[OperationContract]
List<Contact> GetContacts();
}
The list class is defined as:
public interface ICollection<T> : IEnumerable<T>
{...}
public interface IList<T> : ICollection<T>
{...}
[Serializable]
public class List<T> : IList<T>
{
public void Add(T item);
//More members
}
Because it is a valid collection and it has an Add() method, the resulting representation
of the contract will be:
[ServiceContract]
interface IContactManager
{
[OperationContract]
void AddContact(Contact contact);
[OperationContract]
Contact[] GetContacts();
}
That is, a List<Contact> is marshaled as a
Contact[]. The service may still
return a List<Contact>, and
yet the client will interact with an array, as shown in Example 1.
Example 1. Marshaling a list as an array
/////////////////////////// Service Side ////////////////////////////// [ServiceContract] interface IContactManager { [OperationContract] void AddContact(Contact contact);
[OperationContract] List<Contact> GetContacts(); } //Service implementation class ContactManager : IContactManager { List<Contact> m_Contacts = new List<Contact>();
public void AddContact(Contact contact) { m_Contacts.Add(contact); }
public List<Contact> GetContacts() { return m_Contacts; } } /////////////////////////// Client Side ////////////////////////////// [ServiceContract] interface IContactManager { [OperationContract] void AddContact(Contact contact);
[OperationContract] Contact[] GetContacts(); } class ContactManagerClient : ClientBase<IContactManager>,IContactManager { public Contact[] GetContacts() { return Channel.GetContacts(); } //More members } //Client code ContactManagerClient proxy = new ContactManagerClient(); Contact[] contacts = proxy.GetContacts(); proxy.Close();
|
Note that while the collection must have the Add() method to be
marshaled as an array, the collection need not implement the Add() method at all.
2. Custom Collections
It’s not just the built-in collections that can be
marshaled automatically as arrays—any custom collection can abide by
the same prerequisites and be marshaled as an array, as shown in Example 2. In this
example, the collection MyCollection<string> is marshaled as a
string[].
Example 2. Marshaling a custom collection as an array
/////////////////////////// Service Side ////////////////////////////// [Serializable] public class MyCollection<T> : IEnumerable<T> { public void Add(T item) {}
IEnumerator<T> IEnumerable<T>.GetEnumerator() {...} //Rest of the implementation } [ServiceContract] interface IMyContract { [OperationContract] MyCollection<string> GetCollection(); }
/////////////////////////// Client Side ////////////////////////////// [ServiceContract] interface IMyContract { [OperationContract] string[] GetCollection(); }
|